﻿using CuoDing.Core.Common;
using CuoDing.Core.Model.Enum;
using CuoDing.Core.Model.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace CuoDing.Core.WebApi
{
    /// <summary>
    /// 请求上下文中间件
    /// </summary>
    public class HttpContextMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<HttpContextMiddleware> _logger;
        private readonly string noEncryptAction = "[/api/Login/GetToken];";//不参与解密的接口
        private readonly string aeskey = "OiISGKCzs0dlRlHl";//aes密钥
        public HttpContextMiddleware(RequestDelegate requestDelegate, ILogger<HttpContextMiddleware> logger)
        {
            _next = requestDelegate;
            _logger = logger;
        }

        public async Task Invoke(HttpContext context)
        {
            // 替换原本的 Response.Body 流在 _next(context) 执行下一个中间件后，需要读取数据，原本的流不可读 canReader = false
            var originalResponseStream = context.Response.Body;
            using var replaceResponseStream = new MemoryStream();
            context.Response.Body = replaceResponseStream;
            var path = "[" + context.Request.Path + "]";
            if (context.Request.Method.Equals(Microsoft.AspNetCore.Http.HttpMethods.Post, StringComparison.CurrentCultureIgnoreCase) && !noEncryptAction.Contains(path))
            {
                // 过滤请求
                await FilterRequest(context, originalResponseStream);
            }
            await _next(context);
            if (context.Response.StatusCode != StatusCodes.Status200OK)
                return;

            // 过滤响应
            await FilterResponse(context, originalResponseStream, replaceResponseStream);
        }
        /// <summary>
        /// 过滤请求
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private async Task<HttpContext> FilterRequest(HttpContext context, Stream originalResponseStream)
        {
            try
            {
                var requestData = string.Empty;
                //将请求 Body Stream读取并解密
                using (var reader = new StreamReader(context.Request.Body))
                {
                    requestData = await reader.ReadToEndAsync();
                    var dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(requestData);
                    if (dict != null & dict.ContainsKey("data"))
                    {
                        requestData = dict["data"].ToString();
                    }
                    requestData = AESHelper.AesDecrypt(requestData, aeskey);//解密json数据
                    if (string.IsNullOrEmpty(requestData))
                    {
                        await originalResponseStream.WriteAsync(Encoding.Default.GetBytes(JsonConvert.SerializeObject(new DXResult { code = DXCode.BadRequest, msg = "解密数据异常" })));
                    }
                }
                var requestStringContent = new StringContent(requestData);
                context.Request.Body = await requestStringContent.ReadAsStreamAsync();
            }
            catch (Exception ex)
            {

                // 将返回的 Response 流 Copy 到原始流
                await originalResponseStream.WriteAsync(Encoding.Default.GetBytes(JsonConvert.SerializeObject(new DXResult { code = DXCode.BadRequest, msg = "请求数据异常" })));
                context.Response.Body = originalResponseStream;
                _logger.LogError("参数解密异常：{@ex}", ex);
                //await context.Response.WriteAsJsonAsync<DXResult>(new DXResult { code = DXCode.BadRequest, msg = "请求数据异常" });
            }
            return context;
        }

        /// <summary>
        /// 过滤响应
        /// </summary>
        /// <param name="context"></param>
        /// <param name="originalResponseStream"></param>
        /// <param name="replaceResponseStream"></param>
        /// <returns></returns>
        private async Task FilterResponse(HttpContext context, Stream originalResponseStream, MemoryStream replaceResponseStream)
        {
            try
            {
                var responseData = string.Empty;
                using (var reader = new StreamReader(replaceResponseStream))
                {
                    context.Response.Body.Seek(0, SeekOrigin.Begin);
                    responseData = await reader.ReadToEndAsync();
                    //对data里面的数据进行加密
                    var dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseData);
                    if (dict != null & dict.ContainsKey("data"))
                    {
                        var data = dict["data"];
                        var aeskey = "OiISGKCzs0dlRlHl";
                        if (data != null)
                        {
                            var newData = AESHelper.AesEncrypt(JsonConvert.SerializeObject(data), aeskey);
                            dict.Remove("data");
                            dict.Add("data", newData);
                        }
                        responseData = JsonConvert.SerializeObject(dict);
                    }
                }
                // 将返回的 Response 流 Copy 到原始流
                var dataByte = Encoding.Default.GetBytes(responseData.ToString());
                context.Response.ContentType = "application/json";
                context.Response.Headers.Remove("Content-Length");
                context.Response.Headers.Add("Content-Length", new[] { dataByte.Length.ToString() });
                await originalResponseStream.WriteAsync(dataByte,0, dataByte.Length);
                context.Response.Body = originalResponseStream;
            }
            catch (Exception ex)
            {
                _logger.LogError("参数加密异常：{@ex}", ex);
                await context.Response.WriteAsJsonAsync<DXResult>(new DXResult { code = DXCode.BadRequest, msg = "响应数据异常" });
            }
        }
    }
}
